
package Estructuras;

import java.util.Observable;
import java.util.Observer;

/**
 *
 * @author Luis E Sosa S
 */
public class Cpu_struct  implements Observer {

  //Bandera que indica si llego un proceso de mayor prioridad y el cpu
  //necesita reformular
  //0 no hace falta reformular, 1 hace falta reformular
  private boolean  need_resched;

  //Proceso en cpu
  private Task_struct proceso;

  //CUANTUM
  private final static int cuantum = Configuracion.cuantum;;
  //cuantum del proceso en ejecucion
  private static int cuantumPro;

  //politica
  long politica;

  //Ciclo del cpu en el que se encuentra
  long CicloCpu = 0;


  //HACE FALTA UNA REFERENCIA A SCHEDULL para solicitar proceso
  Scheduler sh;



  /**
   Guarda la referencia al shchedull
   */
  public Cpu_struct(Scheduler sh) {
    this.sh = sh;
    this.need_resched = false;
    this.sh.referenciaCpu(this);
  }






  //metodo que se ejecuta al momento de que el objeto observado cambie
  //o1 es el valor pasado por parametro en la this.notifyObservers
  public void update(Observable o, Object o1) {
     System.out.println("CPU recibe señal");
     //Actualiza el ciclo del relog del cpu
     this.CicloCpu = (Integer)o1;
     //AQUI PODRIAMOS COLOCAR UN HILO PARA EJECUTAR
     //si hay proceso en el cpu
     cpu();
  }



  /**
   * Codigo que ejecutara cpu
   */
  public void cpu(){
    //si hay proceso en el cpu
     if(this.proceso != null){
        this.disminuirCpu();
        Cpu_struct.cuantumPro--;
        System.out.println("Proceso "+this.proceso.getPid() +" tengo por " +
                "ejecutar en cpu:" + this.proceso.getCpuTime() + " y un cuantum de: "
                +Cpu_struct.cuantum);
        //si tiempo de cpu es 0
        if(this.proceso.getCpuTime() <= 0){
             //si necesita hacer IO
             if(this.proceso.getIOTime() != 0){
                 //marca proceso como wait y lo agrega a la cola de wait
                 this.marcarProWait();
                 //carga otro proceso en cpu
                 solicitarPro();
           }//Si no necesita hacer IO y cpu = 0
           else{
              //Marcar proceso como muerto
              this.actualizarEstadistica();
              this.marcarProEliminar();
              
              solicitarPro();
           }
        }//Si tiempo de CPU es mayor que 0
        else{
           cpuMayorCero();
        }
     }//Proceso null
     else{
        solicitarPro();
     }

  }


  /**
   * Metodo a ejecutar cuando al cpu le falta por ejectura cputime>0
   */
  public void cpuMayorCero(){
    //revisa si un proceso con mayor prioridad se quiere ejecutar
    if(this.need_resched){
              //le dice a schedule para guardar el proceso en su cola de expropiados
              this.solicitarPro();
    }
    else{
        //revisa politica RR  = 6
        if(this.politica == 6 ){
            //revisa el cuantum para ver si puede seguir ejecutandose
            if(Cpu_struct.cuantumPro >0){
              return;//continua ejecucion
              }
            else{//Si se le acabo el cuantum
              //se agrega el proceso a lista de expirados
              this.sh.yield_task(this.proceso);
              //solicita un nuevo proceso para ejecutar
              solicitarPro();

            }
           }
        // politica Normal o FIFO o REALTIME
        else if (this.politica == 5 || this.politica == 7){
            //Revisa si ha llegado un proceso de mayor prioridad
               if(this.need_resched ){
                 //le dice a schedule para guardar el proceso en su cola de expropiados
                 this.solicitarPro();
              }
      }
    }
  }




/**
 * Metodo para solicitar un nuevo proceso
 * retorna 1 si existe un proceso por ejecutar
 * retorna 0 si no hay proceso por ejecutar (scheduler no retorna proceso)
 */
  private void solicitarPro(){
     //actualiza el tiempo de salida del proceso
     if(this.proceso != null) //si hay un proceso en el cpu
         this.proceso.getSE().setTiempoSale(CicloCpu);
     Task_struct ts;
     //solicita a schedull otro proceso
     ts = this.schedull();
     //Si existe proceso a ejecutar
     if(ts != null){
        //actualiza tiempo de espera del proceso
       ts.getSE().setWaitToExec(CicloCpu - ts.getSE().getTiempoSale());
         
        //asigna proceso a CPU
        this.proceso = ts;
        //apaga bandera de expropiacion si estaba prendida
        this.need_resched = false;
        //cambia politica
        this.politica = ts.getPolicy();
        //reasigna el cuantum del proceso
        Cpu_struct.cuantumPro = cuantum;
        //actualiza el tiempo que estuvo en espera
        
        long tiempoEspera = CicloCpu - ts.getSE().getTiempoSale();
        ts.getSE().setWaitToExec(ts.getSE().getWaitToExec() + tiempoEspera);
        //tiempo en el que inicia a ejecutarse
        //ts.getSE().setExecStart(CicloCpu);
          
         
        System.out.println("Carga en cpu proceso "+ ts.getPid());

        //Imprime algunas estadísticas
        ts.imprimirStadistica();
     }
     else{
          System.out.println("No hay procesos en Scheduller");
          this.proceso = null;
          System.out.println("CPU OCIOSO!!");

     }
  }



  /*
   * Disminuye el tiempo de cpu del proceso en ejecucion
   */
  public int disminuirCpu(){
    int cpuTime;
    cpuTime = this.proceso.disminuirCpu();
    return cpuTime;
  }


  /**          POR IMPLEMENTAR
   * Marca el proceso en el cpu como wait y lo
   * agrega a la cola de wait
   */
  public void marcarProWait(){
     this.proceso.setState(Task_struct.BLOCKED); // WAIT
     //Agregar proceso a cola wait. La cola se encuentra en Scheduler
      sh.addWaitProcess(proceso);
    //Actualizar estadistica
    //desmarca el scheduler class
  }


  /**
   * El proceso es marcado para que sea sacado de la tabla task_list
   */
  public void marcarProEliminar(){
    
    this.proceso.setState(Task_struct.TERMINATED);
  }

/**
 * Saca proceso del cpu
 */
 // private void liberarCpu(){
   // this.proceso = null;
 // }


  /**
   * Metodo usado para actualizar estadistica del proceso
   */
  public void actualizarEstadistica(){

  }




  /**
   *Llama al schedull para que retorne el siguiente proceso a ejecutar
   */
  public Task_struct schedull(){
      return sh.pick_next_task();

  }



  /**
   * Enciende la bandere para needReched
   */
  public void needReched(){
      this.need_resched = true;
  }

  /*
   * Retorna la prioridad del proceso que esta en el cpu
   **/
  public short prioridadProceso(){
    if(this.proceso != null){
        return this.proceso.getPrio();
    }
    //retorna prioridad minima
    return 140;
  }



}